package com.gitee.l0km.codegen.decorator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

import com.gitee.l0km.codegen.base.CodeGenUtils;
import com.gitee.l0km.codegen.base.generator.GeneratorConfiguration;
import com.gitee.l0km.com4j.base.ClassLoaderUtils;

import static com.gitee.l0km.com4j.base.MiscellaneousUtils.elementsOf;
import com.gitee.l0km.javadocreader.JavadocReader;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;

/**
 * decorator生成器参数对象
 * @author guyadong
 *
 */
public class DecoratorConfiguration extends GeneratorConfiguration implements DecoratorConstants{
	public static final String DECORATOR_FOLDER = "decorator";
	/** refClass 的默认值 */
	public static final Class<Object> DEF_REF_CLASS = Object.class;
	private List<Class<?>> interfaceClasses;
	/**
	 * first element in {@link #interfaceClasses}
	 */
	private Class<?> interfaceClass;
	private List<Class<?>> refClasses;
	/**
	 * first element in {@link #refClasses}
	 */
	private Class<?> refClass;
	private List<String> implClasses;
	/**
	 * first element in {@link #refClasses}
	 */
	private String implClass;
	private String sourcepath;
	private String classpath;
	/**
	 * exclude annotation Class names which will be skip for generation
	 */
	private Set<String> excludeAnnotations;
	/**
	 * use Response family class in common-java(com.gitee.l0km:common-base:2.2.0 or above) in spring.controller.class.vm,
	 * if false, generate Response family class in Spring Controller class source 
	 */
	private boolean useCommonResponse = false;
	/**
	 * prefix of path for service port,default is implement class name
	 */
	private String portPrefix;
	private boolean timecost;
	private boolean withDelegateMethod;
	private boolean withV1Compatibility;
	public DecoratorConfiguration() {
		super();
		// 指定模板文件夹
		this.defaultValue.setProperty(TEMPLATE_FOLDER_OPTION_LONG, DECORATOR_FOLDER);
		// 指定refClass的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(REFERENCE_CLASS_OPTION_LONG,DEF_REF_CLASS);
		// 指定implClass的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(IMPL_CLASS_OPTION_LONG,"");
		// 指定sourcepath的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(SOURCE_PREFIX_OPTION_LONG,"");
		// 指定classpath的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(CLASS_PATH_OPTION_LONG,"");
		// 指定useCommonResponse的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(USE_COMMON_RESPONSE_OPTION_LONG, false);
		// 指定excludeAnnotations的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(EXCLUDE_ANN_OPTION_LONG,"");
		// 指定portPrefix的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(PORT_PREFIX_OPTION_LONG,"");
		// 指定timecost的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(TIMECOST_OPTION_LONG,false);
		// 指定beanfilterDelegate的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(WITH_DELEGATE_METHOD_OPTION_LONG,true);
		// 指定v1Compatibility的默认值，避免默认值为{@code null}
		this.defaultValue.setProperty(WITH_V1_COMPATIBILITY_OPTION_LONG,false);
	}
	private List<String> getStrings(String key){
		return FluentIterable.from(((String)getProperty(key)).split("[,;]")).transform(String::trim).copyInto(new ArrayList<>());
	}
	
	@Override
	public void loadConfig(Options options, CommandLine cmd) throws ParseException {
		super.loadConfig(options, cmd);
		sourcepath = getProperty(SOURCE_PREFIX_OPTION_LONG);
		classpath = ClassLoaderUtils.normalizeClasspath((String)getProperty(CLASS_PATH_OPTION_LONG));
		JavadocReader.setSourcepath(sourcepath);
		JavadocReader.setClasspath(classpath);
		JavadocReader.resetRoot();
		interfaceClasses = getClasses(INTERFACE_CLASS_OPTION_LONG, classpath, false);
		interfaceClass = interfaceClasses.get(0);
		if(getProperty(REFERENCE_CLASS_OPTION_LONG) == DEF_REF_CLASS){
			refClass = DEF_REF_CLASS;
			refClasses = Collections.emptyList();
		}else{
			refClasses = getClasses(REFERENCE_CLASS_OPTION_LONG, classpath, false);
			// 两个列表长度必须相等
			checkState(interfaceClasses.size() == refClasses.size(),
					"Mismatch class name size of argument %s and %s",
					INTERFACE_CLASS_OPTION_LONG,REFERENCE_CLASS_OPTION_LONG);
			refClass = refClasses.get(0);
		}
		if(refClass == DEF_REF_CLASS){
			refClass = null;
		}
		/** 如果没有定义 implement-class 则默认为 interface-class的值 */
		if("".equals(getProperty(IMPL_CLASS_OPTION_LONG))){
			implClass = interfaceClass.getName();
			implClasses = Collections.emptyList();
		}else{
			implClasses = getStrings(IMPL_CLASS_OPTION_LONG);
			// 两个列表长度必须相等
			checkState(interfaceClasses.size() == implClasses.size(),
					"Mismatch class name size of argument %s and %s",
					INTERFACE_CLASS_OPTION_LONG,REFERENCE_CLASS_OPTION_LONG);
			/** 
			 * 用 interfaceClasses对应的元素的类名填充 implClasses中为空的元素
			  */
			for(int i=0;i<implClasses.size();++i) {
				if(implClasses.get(i).isEmpty()) {
					implClasses.set(i, interfaceClasses.get(i).getName());
				}
			}
			implClass = implClasses.get(0);
		}
		useCommonResponse = getProperty(USE_COMMON_RESPONSE_OPTION_LONG);
		excludeAnnotations = Sets.newHashSet(elementsOf(getProperty(EXCLUDE_ANN_OPTION_LONG)));
		portPrefix = getProperty(PORT_PREFIX_OPTION_LONG);
		timecost = getProperty(TIMECOST_OPTION_LONG);
		withDelegateMethod = getProperty(WITH_DELEGATE_METHOD_OPTION_LONG);
		withV1Compatibility = getProperty(WITH_V1_COMPATIBILITY_OPTION_LONG);
	}

	/**
	 * @return interfaceClasses
	 */
	public List<Class<?>> getInterfaceClasses() {
		return interfaceClasses;
	}

	/**
	 * @return interfaceClass
	 */
	public Class<?> getInterfaceClass() {
		return interfaceClass;
	}

	/**
	 * @return refClasses
	 */
	public List<Class<?>> getRefClasses() {
		return refClasses;
	}

	/**
	 * @return refClass
	 */
	public Class<?> getRefClass() {
		return refClass;
	}
	/**
	 * @since 2.3.3
	 */
	public List<String> getImplClassNames() {
		return implClasses;
	}
	/**
	 * @since 2.3.3
	 */
	public String getImplClassName() {
		return implClass.replace('$', '.');
	}
	/**
	 * @since 2.3.3
	 */
	public Class<?> getImplClass() {
		try {
			return CodeGenUtils.forName(implClass);
		} catch (Exception e) {
			// DO NOTHING
		}
		return null;
	}
	/**
	 * @return sourcepath
	 */
	public String getSourcepath() {
		return sourcepath;
	}

	/**
	 * @return classpath
	 */
	public String getClasspath() {
		return classpath;
	}

	/**
	 * @return useCommonResponse
	 */
	public boolean isUseCommonResponse() {
		return useCommonResponse;
	}

	public Set<String> getExcludeAnnotations() {
		return excludeAnnotations;
	}

	public String getPortPrefix() {
		return portPrefix;
	}
	/**
	 * @since 2.3.0
	 */
	public boolean isTimecost() {
		return timecost;
	}
	/**
	 * @since 2.3.2
	 */
	public boolean isWithDelegateMethod() {
		return withDelegateMethod;
	}
	/**
	 * @since 3.4.0
	 */
	public boolean isWithV1Compatibility() {
		return withV1Compatibility;
	}
}
